using System;
using System.Collections;
using System.Text;

using Mono.Cecil;
using Mono.Cecil.Cil;

using CILUtil;

namespace CILFlow
{
    public class Flow
    {
        public Flow(MethodDefinition md, Instruction s, Instruction e)
        {
            Start = s;
            End = e;
            this.MD = md;
        }
        public Instruction Start;
        public Instruction End;
        public MethodDefinition MD;
    }

    public class StatementFlow
    {
        public StatementFlow( Instruction s )
        {
            Statement = s;
        }
        public Instruction Statement;
    }

    public class ControlFlow
    {
        public ControlFlow(ArrayList predicates, ArrayList affected)
        {
            this.Predicates = predicates;
            this.Affected = affected;
        }
        public ArrayList Predicates;
        public ArrayList Affected;
        public string Encode()
        {
            return EncodeObjects(this.Predicates) + "<CF->" +
                   EncodeObjects(this.Affected);
        }
        private string EncodeObjects(ArrayList objects)
        {
            string[] strObjs = FormatEntity.Encode(objects);
            return "[" + string.Join("!,!", strObjs) + "]";
        }
    }

    public class FlowBuilder
    {
        public static ArrayList ConstructFlow(MethodDefinition md, PopTree tree)
        {
            ArrayList flows = new ArrayList();
            ConstructFlow(md, tree, flows);
            return flows;
        }
        private static void ConstructFlow( MethodDefinition md, PopTree tree, ArrayList flows)
        {
            foreach (PopTree child in tree._PopList)
            {
                object childEntity  = Entity.GetEntity(md, child._instruction);
                object parentEntity = Entity.GetEntity(md, tree._instruction);
                if (childEntity != null && parentEntity != null)
                {
                    flows.Add(new Flow(md,child._instruction, tree._instruction));
                }
                ConstructFlow(md, child, flows);
            }
        }

        // Similiar to control dependance.
        public static ArrayList BlockFlow(MethodDefinition md, CILFlowGraph graph, Hashtable blockPopTreeListMap)
        {
            ArrayList flows = new ArrayList();
            foreach (CILInstructionBlock block in graph.Blocks)
            {
                PopTreeList list = (PopTreeList)blockPopTreeListMap[block];
                ArrayList predicates = new ArrayList();
                foreach (PopTree tree in list.PopTrees)
                {
                    ArrayList pred = tree.Predicate(md);
                    if( pred != null )
                        predicates.AddRange( pred );
                }
                if (predicates != null && predicates.Count > 0 )
                {
                    Hashtable superEntities = new Hashtable();
                    foreach (CILInstructionBlock childblock in block.Children)
                    {
                        PopTreeList childlist = (PopTreeList)blockPopTreeListMap[childblock];
                        foreach (PopTree childtree in childlist.PopTrees)
                        {
                            ArrayList entities = childtree.CollectEntities(md);
                            foreach (object entity in entities)
                            {
                                superEntities[entity] = entity;
                            }
                        }
                    }
                    // Emit...
                    if (superEntities.Values.Count > 0)
                    {
                        flows.Add(new ControlFlow(predicates, new ArrayList(superEntities.Values)));
                    }
                }
            }
            return flows;
        }
    }
}
